package com.lap.watchdog.starter.core;

import com.lap.framework.tools.JsonUtil;
import com.lap.framework.tools.Md5Util;
import com.lap.watchdog.starter.annotation.RepeatSubmit;
import com.lap.watchdog.starter.exception.ParameterException;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.ConcurrentReferenceHashMap;

import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentMap;

@Slf4j
@UtilityClass
public class RepeatKeyGenerator {
  private static final ParameterNameDiscoverer DISCOVERER = new DefaultParameterNameDiscoverer();
  private static final ExpressionParser PARSER = new SpelExpressionParser();
  private static final ConcurrentMap<Method, String[]> PARAM_NAME_CACHE =
      new ConcurrentReferenceHashMap<>(256);
  private static final ConcurrentMap<String, Expression> EXPRESSION_CACHE =
      new ConcurrentReferenceHashMap<>(256);

  public static String generateKey(ProceedingJoinPoint joinPoint, RepeatSubmit repeat) {
    MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    Method method = signature.getMethod();
    Object[] args = joinPoint.getArgs();
    String expression = repeat.value();

    String keyContent =
        StringUtils.isNotBlank(expression)
            ? resolveElExpression(expression, method, args)
            : generateDefaultKey(args);

    return method.getName() + ":" + Md5Util.md5(keyContent);
  }

  // 5. 解析 SpEL 表达式（带缓存）
  private static String resolveElExpression(String expression, Method method, Object[] args) {
    try {
      Expression elExpr = EXPRESSION_CACHE.computeIfAbsent(expression, PARSER::parseExpression);
      EvaluationContext context = createEvaluationContext(method, args);
      return elExpr.getValue(context, String.class);
    } catch (Exception e) {
      log.error("解析 SpEL 表达式失败: {}", expression, e);
      throw new IllegalArgumentException("无效的 SpEL 表达式: " + expression, e);
    }
  }

  private static EvaluationContext createEvaluationContext(Method method, Object[] args) {
    String[] paramNames = PARAM_NAME_CACHE.computeIfAbsent(method, DISCOVERER::getParameterNames);
    if (ArrayUtils.isEmpty(paramNames)) {
      throw new ParameterException("无法获取方法参数名: " + method);
    }

    StandardEvaluationContext context = new StandardEvaluationContext();
    for (int i = 0; i < paramNames.length; i++) {
      context.setVariable(paramNames[i], args[i]);
    }
    return context;
  }

  private static String generateDefaultKey(Object[] args) {
    return JsonUtil.toJson(args);
  }
}
